﻿//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Expenses;
using Expenses.ExpenseService;
using Windows.UI.Xaml;

namespace Expenses.ViewModels
{
    public class SummaryItemsViewModel : ViewModelBase
    {
        #region Properties

        public ObservableCollection<SortType> SortTypes { get; set; }

        public ObservableCollection<GroupInfoList<object>> GroupedSummaryItems
        {
            get
            {
                return groupedSummaryItems;
            }

            set
            {
                groupedSummaryItems = value;
                RaisePropertyChanged("GroupedSummaryItems");
            }
        }

        public ObservableCollection<SummaryItemViewModel> SummaryItems
        {
            get
            {
                return summaryItems;
            }

            set
            {
                summaryItems = value;
                RaisePropertyChanged("Items");
            }
        }

        public SortType SortType
        {
            get
            {
                return sortType;
            }

            set
            {
                if (sortType == value)
                {
                    return;
                }

                sortType = value;
                RaisePropertyChanged("SortType");

                //update the grouping because the sort/filter type changed
                GroupItems();
            }
        }

        private int numberOfCharges = 0;
        public int NumberOfCharges
        {
            get
            { return numberOfCharges; }

            set
            {
                if (numberOfCharges == value)
                { return; }

                numberOfCharges = value;
                RaisePropertyChanged("NumberOfCharges");
            }
        }

        private decimal amountOfCharges = 0M;
        public decimal AmountOfCharges
        {
            get
            { return amountOfCharges; }

            set
            {
                if (amountOfCharges == value)
                { return; }

                amountOfCharges = value;
                RaisePropertyChanged("AmountOfCharges");
            }
        }

        private int numberOfChargesLT30Days = 0;
        public int NumberOfChargesLT30Days
        {
            get
            { return numberOfChargesLT30Days; }

            set
            {
                if (numberOfChargesLT30Days == value)
                { return; }

                numberOfChargesLT30Days = value;
                RaisePropertyChanged("NumberOfChargesLT30Days");
            }
        }

        private decimal amountOfChargesLT30Days = 0M;
        public decimal AmountOfChargesLT30Days
        {
            get
            { return amountOfChargesLT30Days; }

            set
            {
                if (amountOfChargesLT30Days == value)
                { return; }

                amountOfChargesLT30Days = value;
                RaisePropertyChanged("AmountOfChargesLT30Days");
            }
        }

        private int numberOfCharges30To44Days = 0;
        public int NumberOfCharges30To44Days
        {
            get
            { return numberOfCharges30To44Days; }

            set
            {
                if (numberOfCharges30To44Days == value)
                { return; }

                numberOfCharges30To44Days = value;
                RaisePropertyChanged("NumberOfCharges30To44Days");
            }
        }

        private decimal amountOfCharges30To44Days = 0M;
        public decimal AmountOfCharges30To44Days
        {
            get
            { return amountOfCharges30To44Days; }

            set
            {
                if (amountOfCharges30To44Days == value)
                { return; }

                amountOfCharges30To44Days = value;
                RaisePropertyChanged("AmountOfCharges30To44Days");
            }
        }

        private int numberOfChargesGT45Days = 0;
        public int NumberOfChargesGT45Days
        {
            get
            { return numberOfChargesGT45Days; }

            set
            {
                if (numberOfChargesGT45Days == value)
                { return; }

                numberOfChargesGT45Days = value;
                RaisePropertyChanged("NumberOfChargesGT45Days");
            }
        }

        private decimal amountOfChargesGT45Days = 0M;
        public decimal AmountOfChargesGT45Days
        {
            get
            { return amountOfChargesGT45Days; }

            set
            {
                if (amountOfChargesGT45Days == value)
                { return; }

                amountOfChargesGT45Days = value;
                RaisePropertyChanged("AmountOfChargesGT45Days");
            }
        }

        private DateTime beginDateOfCharges;
        public DateTime BeginDateOfCharges
        {
            get
            { return beginDateOfCharges; }

            set
            {
                if (beginDateOfCharges == value)
                { return; }

                beginDateOfCharges = value;
                RaisePropertyChanged("BeginDateOfCharges");
            }
        }

        private DateTime endDateOfCharges;
        public DateTime EndDateOfCharges
        {
            get
            { return endDateOfCharges; }

            set
            {
                if (endDateOfCharges == value)
                { return; }

                endDateOfCharges = value;
                RaisePropertyChanged("EndDateOfCharges");
            }
        }

        private int numberOfSavedReports = 0;
        public int NumberOfSavedReports
        {
            get
            { return numberOfSavedReports; }

            set
            {
                if (numberOfSavedReports == value)
                { return; }

                numberOfSavedReports = value;
                RaisePropertyChanged("NumberOfSavedReports");
            }
        }

        private decimal amountOfSavedReports = 0M;
        public decimal AmountOfSavedReports
        {
            get
            { return amountOfSavedReports; }

            set
            {
                if (amountOfSavedReports == value)
                { return; }

                amountOfSavedReports = value;
                RaisePropertyChanged("AmountOfSavedReports");
            }
        }

        private int numberOfPendingReports = 0;
        public int NumberOfPendingReports
        {
            get
            { return numberOfPendingReports; }

            set
            {
                if (numberOfPendingReports == value)
                { return; }

                numberOfPendingReports = value;
                RaisePropertyChanged("NumberOfPendingReports");
            }
        }

        private decimal amountOfPendingReports = 0M;
        public decimal AmountOfPendingReports
        {
            get
            { return amountOfPendingReports; }

            set
            {
                if (amountOfPendingReports == value)
                { return; }

                amountOfPendingReports = value;
                RaisePropertyChanged("AmountOfPendingReports");
            }
        }

        private int numberOfReportsNeedingApproval = 0;
        public int NumberOfReportsNeedingApproval
        {
            get
            { return numberOfReportsNeedingApproval; }

            set
            {
                if (numberOfReportsNeedingApproval == value)
                { return; }

                numberOfReportsNeedingApproval = value;
                RaisePropertyChanged("NumberOfReportsNeedingApproval");
            }
        }

        private decimal amountOfReportsNeedingApproval = 0M;
        public decimal AmountOfReportsNeedingApproval
        {
            get
            { return amountOfReportsNeedingApproval; }

            set
            {
                if (amountOfReportsNeedingApproval == value)
                { return; }

                amountOfReportsNeedingApproval = value;
                RaisePropertyChanged("AmountOfReportsNeedingApproval");
            }
        }


        #endregion

        private SortType sortType = SortType.ItemType;
        private ObservableCollection<GroupInfoList<object>> groupedSummaryItems;
        private ObservableCollection<SummaryItemViewModel> summaryItems;
        decimal amountOwedToCreditCard = 0M;
        decimal amountOwedToEmployee = 0M;

        public SummaryItemsViewModel()
        {
            this.GroupedSummaryItems = new ObservableCollection<GroupInfoList<object>>();
            this.SortTypes = new ObservableCollection<SortType>();
            this.SortTypes.Add(SortType.Age);
            this.SortTypes.Add(SortType.Amount);
        }

        public async Task<ObservableCollection<SummaryItemViewModel>> GetSummaryItems()
        {
            var expensesService = new ExpenseServiceClient();
            List<SummaryItem> items = await
                 expensesService.GetSummaryItemsIncludingNullsAsync(App.EmployeeId);

            var summaryItems = from item in items
                               select new SummaryItemViewModel()
                               {
                                   Id = item.Id,
                                   Amount = item.Amount,
                                   Date = Convert.ToDateTime(item.Date),
                                   Description = item.Description,
                                   Submitter = item.Submitter,
                                   ItemType = (Expenses.ItemType)item.ItemType,
                               };
            this.SummaryItems = new ObservableCollection<SummaryItemViewModel>(summaryItems);

            numberOfCharges =
                (from item in this.SummaryItems
                 where item.ItemType == ItemType.Charge
                 select item).Count();
            amountOfCharges =
                (from item in this.SummaryItems
                 where item.ItemType == ItemType.Charge
                 select Convert.ToDecimal(item.Amount)).Sum();
            numberOfChargesLT30Days =
                (from item in this.SummaryItems
                 where item.ItemType == ItemType.Charge &&
                 DateTime.Today.Subtract(item.Date).Days < 30
                 select item).Count();
            amountOfChargesLT30Days =
                (from item in this.SummaryItems
                 where item.ItemType == ItemType.Charge &&
                 DateTime.Today.Subtract(item.Date).Days < 30
                 select Convert.ToDecimal(item.Amount)).Sum();
            numberOfCharges30To44Days =
                (from item in this.SummaryItems
                 where item.ItemType == ItemType.Charge &&
                 DateTime.Today.Subtract(item.Date).Days >= 30
                 && DateTime.Today.Subtract(item.Date).Days < 45
                 select item).Count();
            amountOfCharges30To44Days =
                (from item in this.SummaryItems
                 where item.ItemType == ItemType.Charge &&
                 DateTime.Today.Subtract(item.Date).Days >= 30
                 && DateTime.Today.Subtract(item.Date).Days < 45
                 select Convert.ToDecimal(item.Amount)).Sum();
            numberOfChargesGT45Days =
                (from item in this.SummaryItems
                 where item.ItemType == ItemType.Charge &&
                 DateTime.Today.Subtract(item.Date).Days >= 45
                 select item).Count();
            amountOfChargesGT45Days =
                (from item in this.SummaryItems
                 where item.ItemType == ItemType.Charge &&
                 DateTime.Today.Subtract(item.Date).Days >= 45
                 select Convert.ToDecimal(item.Amount)).Sum();

            if (numberOfCharges > 0)
            {
                beginDateOfCharges = (from item in this.SummaryItems
                                      where item.ItemType == ItemType.Charge
                                      select item.Date).Min();
                endDateOfCharges = (from item in this.SummaryItems
                                    where item.ItemType == ItemType.Charge
                                    select item.Date).Max();
            }

            numberOfSavedReports = (from item in this.SummaryItems
                                    where item.ItemType == ItemType.SavedReport
                                    select item).Count();
            amountOfSavedReports = Convert.ToDecimal(
                (from item in summaryItems
                 where item.ItemType == ItemType.SavedReport
                 select item.Amount).Sum());

            numberOfPendingReports = (from item in this.SummaryItems
                                      where item.ItemType == ItemType.PendingReport
                                      select item).Count();
            amountOfPendingReports = Convert.ToDecimal(
                (from item in this.SummaryItems
                 where item.ItemType == ItemType.PendingReport
                 select item.Amount).Sum());

            var expenseReportsViewModel = new ExpenseReportsViewModel();
            amountOwedToCreditCard = await
                expenseReportsViewModel.GetAmountOwedToCreditCard(
                App.EmployeeId);
            amountOwedToEmployee = await
                expenseReportsViewModel.GetAmountOwedToEmployee(
                App.EmployeeId);

            numberOfReportsNeedingApproval = (from item in this.SummaryItems
                                              where item.ItemType == ItemType.UnresolvedReport
                                              select item).Count();
            amountOfReportsNeedingApproval = Convert.ToDecimal(
                (from item in this.SummaryItems
                 where item.ItemType == ItemType.UnresolvedReport
                 select item.Amount).Sum());

            GroupItems();

            return this.SummaryItems;
        }

        public void GroupItems()
        {
            switch (this.SortType)
            {
                case SortType.Age:
                    GroupItemsByTypeAndAge();
                    break;
                case SortType.Amount:
                    GroupItemsByTypeAndAmount();
                    break;
                case SortType.Submitter:
                    GroupItemsBySubmitter();
                    break;
                case SortType.ItemType:
                    GroupItemsByTypeAndAge();
                    break;
                default:
                    break;
            }
        }

        private void GroupItemsByTypeAndAge()
        {
            var query = from item in summaryItems
                        orderby item.ItemType, item.Date ascending
                        group item by item.ItemType into g
                        select new
                        {
                            GroupName = g.Key,
                            Count = g.Count(),
                            Amount = g.Sum(e => e.Amount),
                            Items = g
                        };

            this.GroupedSummaryItems.Clear();
            var groups = new GroupInfoList<object>();
            foreach (var group in query)
            {
                var items = new GroupInfoList<object>();
                string itemTypeName = null;
                ItemType itemType = (ItemType)group.GroupName;
                switch (itemType)
                {
                    case ItemType.Charge:
                        itemTypeName = "Charges";

                        string beginMonth = beginDateOfCharges.ToString("M").Substring(0, 3);
                        int beginDay = beginDateOfCharges.Day;
                        int beginYear = beginDateOfCharges.Year;
                        string endMonth = endDateOfCharges.ToString("M").Substring(0, 3);
                        int endDay = endDateOfCharges.Day;
                        int endYear = endDateOfCharges.Year;

                        if (beginYear == endYear)
                        {
                            items.Details1 =
                                string.Format("{0} {1} - {2} {3}, {4}",
                                beginMonth, beginDay, endMonth, endDay, endYear);
                        }
                        else
                        {
                            items.Details1 =
                                string.Format("{0} {1}, {2} - {3} {4}, {5}",
                                beginMonth, beginDay, beginYear, endMonth, endDay, endYear);
                        }

                        items.Details2 = string.Format("{0} new for {1:C}",
                            numberOfChargesLT30Days.ToString(),
                            amountOfChargesLT30Days);
                        items.Details3 = string.Format("{0} old for {1:C}",
                            numberOfCharges30To44Days.ToString(),
                            amountOfCharges30To44Days);
                        items.Details4 = string.Format("{0} late for {1:C}",
                            numberOfChargesGT45Days.ToString(),
                            amountOfChargesGT45Days);
                        break;
                    case ItemType.SavedReport:
                        itemTypeName = "Saved Reports";
                        break;
                    case ItemType.PendingReport:
                        itemTypeName = "Pending Reports";
                        items.Details1 = string.Format("{0:C} owed to employee",
                            amountOwedToEmployee);
                        items.Details2 = string.Format("{0:C} owed to card",
                            amountOwedToCreditCard);
                        break;
                    case ItemType.UnresolvedReport:
                        itemTypeName = "Needing Approval";
                        break;
                    case ItemType.ApprovedReport:
                        itemTypeName = "Past Reports";
                        break;
                    default:
                        break;
                }
                items.Key = itemTypeName;
                items.Amount = System.Convert.ToDecimal(group.Amount);
                if (group.Amount == 0)
                {
                    items.Summary = "You have none";
                    items.Details1 = string.Empty;
                    items.Details2 = string.Empty;
                    items.Details3 = string.Empty;
                    items.Details4 = string.Empty;
                }
                else
                {
                    items.Summary = string.Format("{0} for {1:C}",
                        group.Count, items.Amount);
                }
                items.ImportList(group.Items);
                if (!(itemTypeName == "Needing Approval" && !App.IsManager))
                {
                    this.GroupedSummaryItems.Add(items);
                }
            }
        }

        internal GroupInfoList<object> GroupItemsBySubmitter()
        {
            var query = from item in this.SummaryItems
                        orderby item.Submitter
                        group item by item.Submitter into g
                        select new
                        {
                            GroupName = g.Key,
                            Count = g.Count(),
                            Amount = g.Sum(e => e.Amount),
                            Items = g
                        };

            var groups = new GroupInfoList<object>();
            foreach (var group in query)
            {
                var items = new GroupInfoList<object>();
                if (group.Count > 0)
                {
                    items.Key = group.GroupName;
                    items.Summary = string.Format("{0} for {1:C}",
                        items.Count, items.Amount);
                }
                else
                {
                    items.Key = group.GroupName;
                }
                items.Amount = System.Convert.ToDecimal(group.Amount);
                foreach (object item in items)
                {
                    items.Add(item);
                }
                groups.Add(items);
            }

            return groups;
        }


        private void GroupItemsByTypeAndAmount()
        {
            var query = from item in SummaryItems
                        orderby item.ItemType, item.Amount descending
                        group item by item.ItemType into g
                        select new
                        {
                            GroupName = g.Key,
                            Count = g.Count(),
                            Amount = g.Sum(e => e.Amount),
                            Items = g
                        };

            this.GroupedSummaryItems.Clear();
            var groups = new GroupInfoList<object>();
            foreach (var group in query)
            {
                var items = new GroupInfoList<object>();
                string itemTypeName = null;
                ItemType itemType = (ItemType)group.GroupName;
                switch (itemType)
                {
                    case ItemType.Charge:
                        itemTypeName = "Charges";
                        break;
                    case ItemType.SavedReport:
                        itemTypeName = "Saved Reports";
                        break;
                    case ItemType.PendingReport:
                        itemTypeName = "Pending Reports";
                        break;
                    case ItemType.UnresolvedReport:
                        itemTypeName = "Needing Approval";
                        break;
                    default:
                        break;
                }
                items.Key = string.Format(
                    "{0} ({1:C})", itemTypeName, group.Amount);
                items.Amount = System.Convert.ToDecimal(group.Amount);
                items.ImportList(group.Items);
                this.GroupedSummaryItems.Add(items);
            }
        }

    }
}
